package info.batcloud.fanli.core.service.impl;

import info.batcloud.fanli.core.dto.UserMissionDTO;
import info.batcloud.fanli.core.entity.Mission;
import info.batcloud.fanli.core.entity.UserMission;
import info.batcloud.fanli.core.enums.MissionStatus;
import info.batcloud.fanli.core.enums.UserMissionStatus;
import info.batcloud.fanli.core.repository.MissionRepository;
import info.batcloud.fanli.core.repository.UserMissionRepository;
import info.batcloud.fanli.core.service.SystemSettingService;
import info.batcloud.fanli.core.service.UserMissionService;
import info.batcloud.fanli.core.settings.MissionSetting;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class UserMissionServiceImpl implements UserMissionService {

    @Inject
    private UserMissionRepository userMissionRepository;

    @Inject
    private MissionRepository missionRepository;

    @Inject
    private SystemSettingService systemSettingService;

    @Override
    @Transactional
    public List<UserMissionDTO> assignMissionToUser(long userId) {
        Date now = new Date();
        List<UserMission> userMissionList = userMissionRepository
                .findByUserIdAndStatusNot(userId, UserMissionStatus.DELETED);
        MissionSetting missionSetting = systemSettingService.findActiveSetting(MissionSetting.class);
        if (missionSetting.getUserMissionAssignNum() <= userMissionList.size()) {
            return toDtoList(userMissionList);
        }
        int num = missionSetting.getUserMissionAssignNum() - userMissionList.size();
        List<Long> missionIds = userMissionList.stream().map(o -> o.getMission().getId()).collect(Collectors.toList());
        List<Mission> candidateList;
        if (missionIds.size() == 0) {
            candidateList = missionRepository.findByStatus(MissionStatus.VALID);
        } else {
            candidateList = missionRepository.findByIdNotInAndStatus(missionIds, MissionStatus.VALID);
        }
        Collections.shuffle(candidateList);
        List<Mission> assignMissionList = new ArrayList<>();
        if (candidateList.size() <= num) {
            assignMissionList.addAll(candidateList);
        } else {
            assignMissionList.addAll(candidateList.subList(0, num));
        }
        List<UserMissionDTO> dtoList = new ArrayList<>();
        for (Mission mission : assignMissionList) {
            UserMission userMission = new UserMission();
            userMission.setCompleteTimes(0);
            userMission.setStatus(UserMissionStatus.IN_PROGRESS);
            userMission.setCreateTime(now);
            userMission.setMission(mission);
            userMission.setUserId(userId);
            userMissionRepository.save(userMission);
            dtoList.add(toDto(userMission));
        }
        return dtoList;
    }

    @Override
    @Transactional
    public List<UserMissionDTO> findInProgressListByUserId(long userId) {
        List<UserMission> missionList = userMissionRepository.findByUserIdAndStatus(userId, UserMissionStatus.IN_PROGRESS);
        MissionSetting missionSetting = systemSettingService.findActiveSetting(MissionSetting.class);
        if (missionList.size() >= missionSetting.getUserMissionAssignNum()) {
            return toDtoList(missionList);
        }
        List<UserMissionDTO> list = toDtoList(missionList);
        list.addAll(this.assignMissionToUser(userId));
        return list;
    }

    @Override
    public List<UserMissionDTO> findInProgressOrCompleteInDay(long userId, Date date) {
        List<UserMission> userMissions = userMissionRepository.findByUserIdAndStatusOrCompleteTimeBetween(userId, UserMissionStatus.IN_PROGRESS, DateUtils.truncate(date, Calendar.DATE),
                DateUtils.truncate(DateUtils.addDays(date, 1), Calendar.DATE));
        Collections.sort(userMissions, (o1, o2) -> {
            if(o1.getStatus() == UserMissionStatus.IN_PROGRESS) {
                return -1;
            }
            return Long.valueOf(o1.getId() - o2.getId()).intValue();
        });
        return toDtoList(userMissions);
    }

    private List<UserMissionDTO> toDtoList(List<UserMission> userMissionList) {
        List<UserMissionDTO> dtoList = new ArrayList<>();
        for (UserMission userMission : userMissionList) {
            dtoList.add(toDto(userMission));
        }
        return dtoList;
    }

    private UserMissionDTO toDto(UserMission userMission) {
        UserMissionDTO dto = new UserMissionDTO();
        dto.setCompleteTimes(userMission.getCompleteTimes());
        dto.setId(userMission.getId());
        dto.setMissionId(userMission.getMission().getId());
        dto.setTitle(userMission.getTitle());
        dto.setUserId(userMission.getUserId());
        dto.setStatus(userMission.getStatus());
        dto.setMissionType(userMission.getMission().getType());
        return dto;
    }
}
